/**
 * LinkedLST, Kellen Donohue
 * May 11, 2011
 * HttpRequest.java - Manages HTTP gets and sends
 */

package edu.washington.cs.labspecimentransport.data;

import java.io.IOException;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONException;
import org.json.JSONObject;

import edu.washington.cs.labspecimentransport.ops.Settings;

public class HttpRequest {

	/**
	 * Listing of possible ways to do a "send"-type request, one which submits
	 * data
	 * 
	 * @author Kellen
	 * 
	 */
	public enum SendMethod {
		POST, PUT
	}

	@SuppressWarnings("unused")
	private static final int LOGIN_SUCCESS_CODE = 200;

	/**
	 * The client used to access the wobsite
	 */
	private HttpClient httpclient;

	/**
	 * The authentication token for the wobsite
	 */
	private String cookie;

	// private static final String webSiteAddressBase =
	private static final String webSiteAddressBase = 
	 "http://50.19.110.56/";        // Staging instance
	//"http://localhost:3000/";     // Local box
	// "http://50.19.102.21:3000/"; // Cruise Control

	private static class HttpRequestHolder {
		public static final HttpRequest INSTANCE = new HttpRequest();
	}

	/**
	 * Singleton - Don't instantiate
	 */
	private HttpRequest() {
	}

	public static HttpRequest getInstance() {
		return HttpRequestHolder.INSTANCE;
	}

	/**
	 * Authenticate the user and save the cookie for future use
	 * 
	 * @param userName
	 *            Username to login with
	 * @param password
	 *            Password to login with
	 * @return True if it's possible login was successful, otherwise false
	 */
	public boolean authenticate(String userName, String password)
			throws ConnectTimeoutException {
		this.httpclient = new DefaultHttpClient();
		HttpParams params = this.httpclient.getParams();
		// Set timeout based on our settings
		HttpConnectionParams.setConnectionTimeout(params,
				Settings.connection_timeout);
		HttpPost request = new HttpPost(HttpRequest.webSiteAddressBase
				+ "home/login.json?account_name=" + userName + "&password="
				+ password);
		boolean success;
		try {
			HttpResponse result = this.httpclient.execute(request);
			if (!userLoggedIn(result.getStatusLine())) {
				return false;
			}
			Header newCookie = result.getFirstHeader("Set-Cookie");
			this.cookie = newCookie.getValue();
			success = true;
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			success = false;
		} catch (IOException e) {
			e.printStackTrace();
			success = false;
		} finally {
			request.abort();
		}
		return success;
	}

	/**
	 * Whether the user successfully logged in
	 * 
	 * @param statusLine
	 *            The status line
	 * @return True if they did, otherwise false
	 */
	private boolean userLoggedIn(StatusLine statusLine) {
		return statusLine.getStatusCode() == 202;
	}

	/**
	 * Request the given url and save the result
	 * 
	 * @param url
	 *            Url to request from - relative to the site base
	 * @return The result of accessing that URL, probably JSON
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public String sendGetRequest(String url) throws ClientProtocolException,
			IOException, ConnectTimeoutException {
		HttpGet request = new HttpGet(HttpRequest.webSiteAddressBase + url);
		request.setHeader("Cookie", this.cookie);

		ResponseHandler<String> handler = new BasicResponseHandler();

		String response = this.httpclient.execute(request, handler);

		return response;
	}

	/**
	 * Send a to the given URL with the given data of the given method
	 * 
	 * @param url
	 *            Address to contact - relative to the site base
	 * @param data
	 *            JSON data to send
	 * @param method
	 *            The method to send with
	 * @return Server's response to the send
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @throws JSONException
	 */
	public String sendRequest(String url, JSONObject data, SendMethod method)
			throws ClientProtocolException, IOException, JSONException,
			ConnectTimeoutException {
		HttpEntityEnclosingRequestBase request;
		// Create the request based on the method
		switch (method) {
		case POST:
			request = new HttpPost(HttpRequest.webSiteAddressBase + url);
			break;
		case PUT:
			request = new HttpPut(HttpRequest.webSiteAddressBase + url);
			break;
		default:
			throw new IllegalArgumentException("Must pick a valid send type");
		}
		request.setHeader("Cookie", this.cookie);
		request.setHeader("Content-Type", "application/json");
		request.setEntity(new StringEntity(data.toString()));

		ResponseHandler<String> handler = new BasicResponseHandler();

		String response = this.httpclient.execute(request, handler);

		return response;
	}
}
